#pragma once

#include <unordered_map>
#include <unordered_set>
#include <vector>

#include "ast.h"
#include "semantics.h"
#include "symbol.h"
#include "visitor.h"

class SemanticAnalyzer : public ASTVisitor {
    Type::Basic retType;
    std::unordered_set<AST::ASTNode*> visited;
    std::vector<AST::Block*> block;

    void Cast(AST::ASTNode*& origin, Type::Basic to);
    Type::Basic HandleImplicitCast(AST::ASTNode*& lhs, AST::ASTNode*& rhs);

    AST::ASTNode* VisitAssignExpr(AST::AssignExpr* node) override;
    AST::ASTNode* VisitCallExpr(AST::CallExpr* node) override;
    AST::ASTNode* VisitArrayExpr(AST::ArrayExpr* node) override;
    AST::ASTNode* VisitOperatorExpr(AST::OperatorExpr* node) override;
    AST::ASTNode* VisitTernaryExpr(AST::TernaryExpr* node) override;
    AST::ASTNode* VisitLiteralExpr(AST::LiteralExpr* node) override;
    AST::ASTNode* VisitVoidExpr(AST::VoidExpr* node) override;
    AST::ASTNode* VisitIdentifierExpr(AST::IdentifierExpr* node) override;
    AST::ASTNode* VisitInitializerExpr(AST::InitializerExpr* node) override;
    AST::ASTNode* VisitCastExpr(AST::CastExpr* node) override;
    AST::ASTNode* VisitAddrExpr(AST::AddrExpr* node) override;
    AST::ASTNode* VisitArgList(AST::ArgList* node) override;
    AST::ASTNode* VisitBlock(AST::Block* node) override;
    AST::ASTNode* VisitVarDecl(AST::VarDecl* node) override;
    AST::ASTNode* VisitFuncDecl(AST::FuncDecl* node) override;
    AST::ASTNode* VisitIfStmt(AST::IfStmt* node) override;
    AST::ASTNode* VisitWhileStmt(AST::WhileStmt* node) override;
    AST::ASTNode* VisitContinueStmt(AST::ContinueStmt* node) override;
    AST::ASTNode* VisitBreakStmt(AST::BreakStmt* node) override;
    AST::ASTNode* VisitReturnStmt(AST::ReturnStmt* node) override;

  public:
    SymbolMap symbolMap;

    SemanticAnalyzer(const std::vector<ExtFunction>& extFuncs) {
        for (auto ext : extFuncs) {
            auto func = new Symbol();
            auto info = new Semantics::Function(ext.retType);
            info->isExternal = true;
            info->hasVA = ext.hasVA;
            info->argType = ext.argType;
            func->name = ext.name;
            func->isGlobal = true;
            func->info = info;
            symbolMap.ext[ext.name] = func;
        }
    }

    void Visit(AST::ASTNode*& node) override;
    void* Run(void* input) override;
};
